package hi_jvm

import (
	"fmt"
	"hi-jvm/instructions"
	"hi-jvm/instructions/base"
	"hi-jvm/instructions/util"
	"hi-jvm/rtda"
	"hi-jvm/rtda/heap"
)

/*
解释器
 */

/*
创建一个thread
创建帧并推入Java虚拟机栈顶
执行方法
 */
func Interpret(method *heap.Method, logInst bool) {
	thread := rtda.NewThread()
	frame := thread.NewFrame(method)
	thread.PushFrame(frame)

	defer catchErr(thread)
	loop(thread, logInst)
}

/*
寻黄执行“计算PC，解码指令， 执行指令”
 */
func loop(thread *rtda.Thread, logInst bool) {
	reader := &util.BytecodeReader{}

	for {
		frame := thread.CurrentFrame()
		pc := frame.NextPC
		thread.PC = pc

		// decode
		reader.Reset(frame.Method.Code, pc)
		opcode := reader.ReadUInt8()
		inst := instructions.NewInstruction(opcode)
		inst.FetchOperands(reader)
		frame.NextPC = reader.PC

		if (logInst) {
			logInstruction(frame, inst)
		}

		// execute
		inst.Execute(frame)

		if thread.IsStackEmpty() {
			break
		}
	}
}

func logInstruction(frame *rtda.Frame, inst base.Instruction) {
	method := frame.Method
	className := method.Class.Name
	methodName := method.Name
	pc := frame.Thread.PC
	fmt.Printf("%v/%v() #%2d %T %v\n", className, methodName, pc, inst, inst)
}

func logFrames(thread *rtda.Thread) {
	if thread.IsStackEmpty() {
		return
	}

	frame := thread.PopFrame()
	method := frame.Method
	className := method.Class.Name
	fmt.Printf(">> pc:%4d %v.%v%v \n",
		frame.NextPC, className, method.Name, method.Descriptor)
}

func catchErr(thread *rtda.Thread) {
	if r := recover(); r != nil {
		logFrames(thread)
		panic(r)
	}
}